package com.bxl.netty.rpc.register.server.common.server;

import com.bxl.netty.rpc.client.common.client.ClientInfo;
import com.bxl.netty.rpc.client.common.pool.ZoneClientPool;
import com.bxl.netty.rpc.client.common.register.CallBackRegister;
import com.bxl.netty.rpc.common.Lifecycle;
import com.bxl.netty.rpc.common.builder.MsgBuilder;
import com.bxl.netty.rpc.common.consts.InnerZoneIdDefine;
import com.bxl.netty.rpc.common.future.CompletableFuture;
import com.bxl.netty.rpc.common.message.PackageMsg;
import com.bxl.netty.rpc.common.message.RegisterContent;
import com.bxl.netty.rpc.common.pool.ThreadPoolUtil;
import com.bxl.netty.rpc.common.util.HandlerFactory;
import com.bxl.netty.rpc.register.server.common.context.ServerContext;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <>
 *
 * @author baixl
 * @date 2021/2/23
 */
public class Server implements Lifecycle {
    // 指定端口号
    private int port = 9999;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Properties properties;
    private ServerContext serverContext;
    private boolean isRegister;

    public Server() {
    }

    /**
     * 根据配置文件进行server的配置
     *
     * @param properties
     */
    public Server(Properties properties, ServerContext context) {
        this.properties = properties;
    }

    public Server(int port, ServerContext context, boolean isRegister) {
        this.port = port;
        this.serverContext = context;
        this.isRegister = isRegister;
    }


    public static void main(String[] args) {
        new Server().start();
    }

    public void start() {
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup(8);
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            // 指定socket的一些属性
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)  // 指定是一个NIO连接通道
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加编解码器, 由于ByteToMessageDecoder的子类无法使用@Sharable注解,
                            // 这里必须给每个Handler都添加一个独立的Decoder.
                            List<HandlerFactory> encoders = serverContext.getEncoder();
                            if (encoders != null) {
                                for (HandlerFactory encoder : encoders) {
                                    pipeline.addLast((ChannelOutboundHandlerAdapter) encoder.getBean(serverContext));
                                }
                            }
                            List<HandlerFactory> decoders = serverContext.getDecoder();
                            if (decoders != null) {
                                for (HandlerFactory decoder : decoders) {
                                    pipeline.addLast((ChannelInboundHandlerAdapter) decoder.getBean(serverContext));
                                }
                            }

                            List<HandlerFactory> clientHadlers = serverContext.getHandler();
                            if (clientHadlers != null) {
                                for (HandlerFactory hadler : clientHadlers) {
                                    pipeline.addLast((ChannelInboundHandlerAdapter) hadler.getBean(serverContext));
                                }
                            }
                        }
                    });


            // 绑定对应的端口号,并启动开始监听端口上的连接
            Channel ch = serverBootstrap.bind(port).sync().channel();
            System.out.printf("luck协议启动地址：127.0.0.1:%d/\n", port);


            if (isRegister) {
                register();
            }


            // 等待关闭,同步端口
            ch.closeFuture().sync();
        } catch (Exception e) {

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public void stop() {
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

    private void register() {
        ZoneClientPool clientPool = serverContext.getClientPool();

        List<ClientInfo> clients = clientPool.getClients(InnerZoneIdDefine.REGISTER.getZoneId());
        final PackageMsg<RegisterContent> registerMsg = MsgBuilder.createMsg(1, serverContext.getApp(), "localhost", port);
        final PackageMsg<RegisterContent> heartBeatMsg = MsgBuilder.createMsg(2, serverContext.getApp(), "localhost", port);

        for (final ClientInfo client : clients) {
            ThreadPoolUtil.submit(new Runnable() {
                public void run() {
                    try {
                        CompletableFuture<Object> cf = new CompletableFuture<Object>();
                        registerMsg.getHeader().setRequestID(Math.abs(UUID.randomUUID().getLeastSignificantBits()));
                        CallBackRegister.register(registerMsg.getHeader().getRequestID(), cf);
                        client.getClient().writeAndFlush(registerMsg);
                    } catch (Exception e) {

                    }
                }
            });

            ThreadPoolUtil.scheduleAtFixedRate(new Runnable() {
                public void run() {
                    try {
                        CompletableFuture<Object> cf = new CompletableFuture<Object>();
                        heartBeatMsg.getHeader().setRequestID(Math.abs(UUID.randomUUID().getLeastSignificantBits()));
                        CallBackRegister.register(heartBeatMsg.getHeader().getRequestID(), cf);
                        client.getClient().writeAndFlush(heartBeatMsg);
                    } catch (Exception e) {

                    }
                }
            }, 5, 5, TimeUnit.SECONDS);

        }


    }
}
